/**
 * @file time_util.cpp
 * @brief To-be-added
 * @copyright Copyright (c) 2020 YUSUR Technology Co., Ltd. All Rights Reserved. Learn more at
 * www.yusur.tech.
 * @author Qiang Wang (wangq@yusur.tech)
 * @date 2021-09-22 19:41:27
 */

#include "base/time_util.h"
#include <cstring>
#include <chrono>

int64_t get_current_timestamp_us()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds> tp =
        std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now());
    return tp.time_since_epoch().count();
}

int64_t get_current_timestamp_ms()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
        std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    return tp.time_since_epoch().count();
}

int64_t get_current_timestamp_s()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> tp =
        std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
    return tp.time_since_epoch().count();
}

int64_t convert_to_timestamp_ms(uint64_t mdtime)
{
    int ms = mdtime % 1000;
    mdtime /= 1000;
    int sec = mdtime % 100;
    mdtime /= 100;
    int min = mdtime % 100;
    mdtime /= 100;
    int hour = mdtime % 100;

    return get_timestamp_today_ms(hour, min, sec) + ms;
}

int64_t convert_to_timestamp_after_basetime_s(uint64_t mdtime)
{
    int sec = mdtime % 100;
    mdtime /= 100;
    int min = mdtime % 100;
    mdtime /= 100;
    int hour = mdtime % 100;
    // 检查合法性
    if (hour > 23 || sec > 59 || min > 59 )
    {
        return -1;
    }
    return ((hour) * 60 + (min)) * 60 + (sec);
}

int64_t convert_to_timestamp_after_basetime_s(int hour, int minute, int second)
{
	// 检查合法性
	if (hour > 23 || second > 59 || minute > 59)
	{
		return -1;
	}
	return ((hour) * 60 + (minute)) * 60 + (second);
}

int64_t get_current_format_date()
{
    char timestamp[16] = {0};

    std::chrono::system_clock::time_point time_point_now =
        std::chrono::system_clock::now();  // 获取当前时间点

    time_t    tt = std::chrono::system_clock::to_time_t(time_point_now);
    struct tm ptm;
#if defined(_WIN32)
    localtime_s(&ptm, &tt);
#elif defined(__unix__) || defined(__linux__)
    localtime_r(&tt, &ptm);
#endif  // WIN32

    sprintf(timestamp, "%04d%02d%02d%", ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday);

    return atol(timestamp);
}

int64_t get_timestamp_from_date_ms(int year, int month, int day, int hour, int minute, int second)
{
    tm temp;
    memset(&temp, 0, sizeof(tm));
    temp.tm_sec  = second;
    temp.tm_min  = minute;
    temp.tm_hour = hour;
    temp.tm_mday = day;
    temp.tm_mon  = month - 1;
    temp.tm_year = year - 1900;
    return mktime(&temp) * 1000;
}

int64_t get_timestamp_today_ms(int hour, int minute, int second)
{
    time_t    tt = get_current_timestamp_s();
    struct tm ptm;
    // thread-safe function
#if defined(_WIN32)
    localtime_s(&ptm, &tt);
#elif defined(__unix__) || defined(__linux__)
    localtime_r(&tt, &ptm);
#endif  // WIN32

    return get_timestamp_from_date_ms(
        ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday, hour, minute, second);
}

std::string get_current_datetime_str(const std::string& format /* = "%04d%02d%02d-%02d%02d%02d" */)
{
    auto      now = std::chrono::system_clock::now();
    auto      tt  = std::chrono::system_clock::to_time_t(now);
    struct tm ptm;

    // thread-safe function
#if defined(_WIN32)
    localtime_s(&ptm, &tt);
#elif defined(__unix__) || defined(__linux__)
    localtime_r(&tt, &ptm);
#endif  // WIN32

    char date[60] = {0};
    sprintf(date, format.c_str(), 1900 + ptm.tm_year, (ptm.tm_mon + 1), ptm.tm_mday, ptm.tm_hour, ptm.tm_min, ptm.tm_sec);

    return std::string(date);
}

std::string timestamp_to_string_us(time_t tt)
{
    struct tm ptm;
    time_t    t_temp = tt / 1000000;
    // thread-safe function
#if defined(_WIN32)
    localtime_s(&ptm, &tt);
#elif defined(__unix__) || defined(__linux__)
    localtime_r(&t_temp, &ptm);
#endif  // WIN32

    char date[60] = {0};
    sprintf(date, "%04d.%02d.%02d %02d:%02d:%02d.%06d", 1900 + ptm.tm_year, (ptm.tm_mon + 1), ptm.tm_mday, ptm.tm_hour, ptm.tm_min, ptm.tm_sec, tt%1000000);

    return std::string(date);
}

void wait_for_us(uint32_t time)
{
    uint64_t old_timestamp     = get_current_timestamp_us();
    uint64_t current_timestamp = get_current_timestamp_us();
    while (current_timestamp - old_timestamp < time)
    {
        current_timestamp = get_current_timestamp_us();
    }
}

std::string get_current_datetime_us_str()
{
    return timestamp_to_string_us(get_current_timestamp_us());
}

bool check_and_convert_to_timestamp_us(uint64_t mdtime, uint64_t& target_timestamp)
{
    int sec = mdtime % 100;
    mdtime /= 100;
    int min = mdtime % 100;
    mdtime /= 100;
    int hour = mdtime % 100;
    // 合法性检查
    if (hour >= 24 || min >= 60 || sec >= 60)
    {
        return false;
    }
    target_timestamp = get_timestamp_today_ms(hour, min, sec) * 1000ULL;
    return true;
}

uint32_t convert_to_seconds_in_day(uint64_t timestamp_us)
{
	using namespace std::chrono;

	// 创建时间点
	time_point<system_clock> tp{ microseconds(timestamp_us) };

	// 转换为time_t
	auto converted_time_t = system_clock::to_time_t(tp);

	// 使用localtime转换为结构化时间
	auto tm = *std::localtime(&converted_time_t);

	// 计算秒数
	uint32_t seconds_in_day = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec;

	return seconds_in_day;
}